package falcon.script;

import java.util.ArrayList;
import java.util.Stack;

import falcon.common.Errno;
import falcon.common.Log;
import falcon.script.FalconLangInstruction.Operand;

public final class FalconLangTranslator {
	
	public FalconLangTranslator() {
		m_stack = new Stack<FalconLangInstruction.Operand>();
		m_allocator = new boolean[FalconLangProcessor.TMP_COUNT];
	}
	
	public void reset() {
		m_x = 0;
		m_block = 0;
		m_stack.clear();
		for (int i = FalconLangProcessor.FIX_TMP_COUNT; i < m_allocator.length; i ++) {
			m_allocator[i] = false;
		}
	}

	public int translate(ArrayList<FalconLangAst> astList, ArrayList<FalconLangInstruction> instructionList) {
		m_instructionList = instructionList;
		while (m_x < astList.size()) {
			FalconLangAst ast = astList.get(m_x);
			int errno = process(ast);
			if (errno != Errno.SUCCESS) {
				return errno;
			}
			m_x ++;
		}
		return Errno.SUCCESS;
	}
	
	private int process(FalconLangAst ast) {
		int errno = Errno.SCRIPT_TRANSLATE_NOT_PERMITTED;
		int type = ast.type;
		if (type == FalconLangAst.SEQ) {
			errno = processSeq(ast);
		} else if (type == FalconLangAst.RET) {
			errno = translateReturn(ast);
		} else if (type == FalconLangAst.INVOKE_FUNC) {
			errno = translateInvoke(ast);
		} else if (type == FalconLangAst.INVOKE_FLOW) {
			errno = translateInvoke(ast);
		} else if (type == FalconLangAst.INVOKE_SYS) {
			errno = translateInvoke(ast);
		} else if (type == FalconLangAst.CONDITION_IF) {
			errno = translateJump(ast, type);
		} else if (type == FalconLangAst.CONDITION_ELIF) {
			errno = translateJump(ast, type);
		} else if (type == FalconLangAst.CONDITION_ELSE) {
			errno = translateJump(ast, type);
		} else if (type == FalconLangAst.CONDITION_END) {
			errno = translateJump(ast, type);
		}
		return errno;
	}
	
	private int processSeq(FalconLangAst ast) {
		if (ast == null) {
			return Errno.SUCCESS;
		}
		FalconLangAst child1 = ast.child1;
		if (child1 == null) {
			Log.log("");
			return Errno.SCRIPT_TRANSLATE_ERROR;
		}
		int errno = Errno.SCRIPT_TRANSLATE_NOT_PERMITTED;
		int type = child1.type;
		if (type == FalconLangAst.ASSIGN) {
			errno = translateAssign(child1);
		} else if (type == FalconLangAst.PASS) {
			errno = translatePass(child1);
		} else if (type == FalconLangAst.INVOKE_FUNC) {
			errno = translateInvoke(child1);
		} else if (type == FalconLangAst.INVOKE_FLOW) {
			errno = translateInvoke(child1);
		} else if (type == FalconLangAst.INVOKE_SYS) {
			errno = translateInvoke(child1);
		}
		if (errno != Errno.SUCCESS) {
			Log.log("");
			return errno;
		}
		return processSeq(ast.child2);
	}
	
	private int processLogic(FalconLangAst ast) {
		if (ast == null) {
			return Errno.SUCCESS;
		}
		FalconLangAst child1 = ast.child1;
		if (child1 == null) {
			Log.log("");
			return Errno.SCRIPT_TRANSLATE_ERROR;
		}
		negation(ast.type != FalconLangAst.LOGIC_OR);
		int errno = processBinaryOperator(child1);
		if (errno != Errno.SUCCESS) {
			Log.log("");
			return errno;
		}
		return processLogic(ast.child2);
	}

	private int processBinaryOperator(FalconLangAst ast) {
		if (ast == null) {
			return Errno.SUCCESS;
		}
		int errno = processBinaryOperator(ast.child1);
		if (errno != Errno.SUCCESS) {
			Log.log("");
			return errno;
		}
		errno = processBinaryOperator(ast.child2);
		if (errno != Errno.SUCCESS) {
			Log.log("");
			return errno;
		}
		int type = ast.type;
		if (type == FalconLangAst.VALUE_CONST) {
			errno = push(ast);
		} else if (type == FalconLangAst.VALUE_TMP) {
			errno = push(ast);
		} else if (type == FalconLangAst.VALUE_VAR) {
			errno = push(ast);
		} else if (type == FalconLangAst.VALUE_ARG) {
			errno = push(ast);
		} else if (type == FalconLangAst.INVOKE_SYS) {
			errno = translateResult(ast);
		} else if (type == FalconLangAst.COMPARE_EQ) {
			errno = translateBinaryOperator(ast, type);
		} else if (type == FalconLangAst.COMPARE_NE) {
			errno = translateBinaryOperator(ast, type);
		} else if (type == FalconLangAst.COMPARE_GT) {
			errno = translateBinaryOperator(ast, type);
		} else if (type == FalconLangAst.COMPARE_GE) {
			errno = translateBinaryOperator(ast, type);
		} else if (type == FalconLangAst.COMPARE_LT) {
			errno = translateBinaryOperator(ast, type);
		} else if (type == FalconLangAst.COMPARE_LE) {
			errno = translateBinaryOperator(ast, type);
		} else if (type == FalconLangAst.OPERATOR_JOIN) {
			errno = translateBinaryOperator(ast, type);
		} else if (type == FalconLangAst.OPERATOR_ADD) {
			errno = translateBinaryOperator(ast, type);
		} else if (type == FalconLangAst.OPERATOR_SUB) {
			errno = translateBinaryOperator(ast, type);
		} else if (type == FalconLangAst.OPERATOR_MUL) {
			errno = translateBinaryOperator(ast, type);
		} else if (type == FalconLangAst.OPERATOR_DIV) {
			errno = translateBinaryOperator(ast, type);
		} else if (type == FalconLangAst.OPERATOR_MOD) {
			errno = translateBinaryOperator(ast, type);
		} else {
			errno = Errno.SCRIPT_TRANSLATE_NOT_PERMITTED;
			Log.log("type=%d", type);
		}
		return errno;
	}
	
	private int translateAssign(FalconLangAst ast) {
		int errno = processBinaryOperator(ast.child2);
		if (errno != Errno.SUCCESS) {
			Log.log("");
			return errno;
		}
		Operand op2 = pop();
		if (op2 == null) {
			Log.log("");
			return Errno.SCRIPT_TRANSLATE_ERROR;
		}
		FalconLangInstruction instruction = new FalconLangInstruction(FalconLangInstruction.MOV);
		instruction.operand1 = convertValue(ast.child1);
		instruction.operand2 = op2;
		add(instruction);
		recycle(op2);
		return Errno.SUCCESS;
	}
	
	private int translatePass(FalconLangAst ast) {
		// 调用的参数传递是通过固定数组来实现的
		// 这里直接复用 assign 的指令结构
		return translateAssign(ast);
	}

	private int translateReturn(FalconLangAst ast) {
		int errno = processSeq(ast.child2);
		if (errno != Errno.SUCCESS) {
			Log.log("");
			return errno;
		}
		FalconLangInstruction instruction = new FalconLangInstruction(FalconLangInstruction.RET);
		instruction.operand1 = convertValue(ast.child1);
		add(instruction);
		return Errno.SUCCESS;
	}

	private int translateInvoke(FalconLangAst ast) {
		int errno = processSeq(ast.child2);
		if (errno != Errno.SUCCESS) {
			Log.log("");
			return errno;
		}
		FalconLangInstruction instruction = new FalconLangInstruction(FalconLangInstruction.CALL);
		instruction.operand1 = convertValue(ast);
		instruction.operand2 = convertValue(ast.child1);
		add(instruction);
		return Errno.SUCCESS;
	}
	
	private int translateJump(FalconLangAst ast, int type) {
		if (type == FalconLangAst.CONDITION_ELIF) {
			markEnd();
		} else if (type == FalconLangAst.CONDITION_ELSE) {
			markEnd();
		}
		int errno = processLogic(ast.child1);
		if (errno != Errno.SUCCESS) {
			Log.log("");
			return errno;
		}
		if (type == FalconLangAst.CONDITION_IF) {
			markBegin();
		} else if (type == FalconLangAst.CONDITION_ELIF) {
			markBegin();
		} else if (type == FalconLangAst.CONDITION_ELSE) {
			markBegin();
		} else if (type == FalconLangAst.CONDITION_END) {
			markEnd();
		}
		return Errno.SUCCESS;
	}
	
	private int translateResult(FalconLangAst ast) {
		int errno = translateInvoke(ast);
		if (errno != Errno.SUCCESS) {
			Log.log("");
			return errno;
		}
		Operand op = allocate();
		FalconLangInstruction instruction = new FalconLangInstruction(FalconLangInstruction.MOV);
		instruction.operand1 = op;
		instruction.operand2 = new Operand(Operand.TMP, "1");
		add(instruction);
		push(op);
		return Errno.SUCCESS;
	}

	private int translateBinaryOperator(FalconLangAst ast, int type) {
		Operand op2 = pop();
		Operand op1 = pop();
		if (op1 == null) {
			Log.log("");
			return Errno.SCRIPT_TRANSLATE_ERROR;
		}
		if (op1.type != Operand.TMP) {
			Operand op = allocate();
			if (op == null) {
				Log.log("");
				return Errno.SCRIPT_TRANSLATE_ERROR;
			}
			FalconLangInstruction instruction = new FalconLangInstruction(FalconLangInstruction.MOV);
			instruction.operand1 = op;
			instruction.operand2 = op1;
			add(instruction);
			op1 = op;
		}
		if (type == FalconLangAst.COMPARE_EQ) {
			return translateTest(op1, op2, FalconLangInstruction.JE, FalconLangInstruction.JNE);
		} else if (type == FalconLangAst.COMPARE_NE) {
			return translateTest(op1, op2, FalconLangInstruction.JNE, FalconLangInstruction.JE);
		} else if (type == FalconLangAst.COMPARE_GT) {
			return translateTest(op1, op2, FalconLangInstruction.JG, FalconLangInstruction.JLE);
		} else if (type == FalconLangAst.COMPARE_GE) {
			return translateTest(op1, op2, FalconLangInstruction.JGE, FalconLangInstruction.JL);
		} else if (type == FalconLangAst.COMPARE_LT) {
			return translateTest(op1, op2, FalconLangInstruction.JL, FalconLangInstruction.JGE);
		} else if (type == FalconLangAst.COMPARE_LE) {
			return translateTest(op1, op2, FalconLangInstruction.JLE, FalconLangInstruction.JG);
		} else if (type == FalconLangAst.OPERATOR_JOIN) {
			return translateCalc(op1, op2, FalconLangInstruction.JOIN);
		} else if (type == FalconLangAst.OPERATOR_ADD) {
			return translateCalc(op1, op2, FalconLangInstruction.ADD);
		} else if (type == FalconLangAst.OPERATOR_SUB) {
			return translateCalc(op1, op2, FalconLangInstruction.SUB);
		} else if (type == FalconLangAst.OPERATOR_MUL) {
			return translateCalc(op1, op2, FalconLangInstruction.MUL);
		} else if (type == FalconLangAst.OPERATOR_DIV) {
			return translateCalc(op1, op2, FalconLangInstruction.DIV);
		} else if (type == FalconLangAst.OPERATOR_MOD) {
			return translateCalc(op1, op2, FalconLangInstruction.MOD);
		}
		return Errno.SCRIPT_TRANSLATE_NOT_PERMITTED;
	}
	
	private int translateTest(Operand op1, Operand op2, int c1, int c2) {
		FalconLangInstruction instruction = new FalconLangInstruction(FalconLangInstruction.TEST);
		instruction.operand1 = op1;
		instruction.operand2 = op2;
		add(instruction);
		// 逻辑 AND 需要翻转命令
		// 逻辑 OR 则不需要
		int code = isNegation() ? c2 : c1;
		String tag = isNegation() ? FalconLangInstruction.Label.BLOCK_END : FalconLangInstruction.Label.BLOCK_BEGIN;
		int id = getBlock() + 1;
		instruction = new FalconLangInstruction(code);
		instruction.operand1 = op1;
		String value = String.format("%s%d", tag, id);
		instruction.operand2 = new Operand(Operand.LABEL, value);
		add(instruction);
		recycle(op1);
		recycle(op2);
		return Errno.SUCCESS;
	}
	
	private int translateCalc(Operand op1, Operand op2, int c) {
		FalconLangInstruction instruction = new FalconLangInstruction(c);
		instruction.operand1 = op1;
		instruction.operand2 = op2;
		add(instruction);
		push(op1);
		recycle(op2);
		return Errno.SUCCESS;
	}
	
	private void push(Operand op) {
		m_stack.push(op);
	}
	
	private int push(FalconLangAst ast) {
		Operand op = convertValue(ast);
		if (op == null) {
			Log.log("");
			return Errno.SCRIPT_TRANSLATE_NOT_PERMITTED;
		}
		push(op);
		return Errno.SUCCESS;
	}
	
	private Operand pop() {
		return m_stack.pop();
	}
	
	private void add(FalconLangInstruction instruction) {
		m_instructionList.add(instruction);
	}
	
	private Operand allocate() {
		int index = -1;
		for (int i = FalconLangProcessor.FIX_TMP_COUNT; i < m_allocator.length; i ++) {
			if (m_allocator[i] == false) {
				m_allocator[i] = true;
				index = i;
				break;
			}
		}
		if (index < 0) {
			Log.log("");
			return null;
		}
		return new Operand(Operand.TMP, String.format("%d", index));
	}
	
	private void recycle(Operand op) {
		if (op.type != Operand.TMP) {
			return;
		}
		int index = Integer.parseInt(op.value);
		if (index < FalconLangProcessor.FIX_TMP_COUNT || index > m_allocator.length - 1) {
			return;
		}
		m_allocator[index] = false;
	}
	
	private void markBegin() {
		incBlock();
		String tag = FalconLangInstruction.Label.BLOCK_BEGIN;
		int id = getBlock();
		String value = String.format("%s%d", tag, id);
		FalconLangInstruction instruction = new FalconLangInstruction(FalconLangInstruction.NOP);
		instruction.operand1 = new Operand(Operand.LABEL, value);
		add(instruction);
	}
	
	private void markEnd() {
		String tag = FalconLangInstruction.Label.BLOCK_END;
		int id = getBlock();
		String value = String.format("%s%d", tag, id);
		FalconLangInstruction instruction = new FalconLangInstruction(FalconLangInstruction.NOP);
		instruction.operand1 = new Operand(Operand.LABEL, value);
		add(instruction);
	}
	
	private void negation(boolean negation) {
		m_negation = negation;
	}
	
	private boolean isNegation() {
		return m_negation;
	}
	
	private int getBlock() {
		return m_block;
	}
	
	private void incBlock() {
		m_block ++;
	}
	
	private static Operand convertValue(FalconLangAst ast) {
		int operandType;
		int type = ast.type;
		if (type == FalconLangAst.VALUE_CONST) {
			operandType = Operand.CONST;
		} else if (type == FalconLangAst.VALUE_TMP) {
			operandType = Operand.TMP;
		} else if (type == FalconLangAst.VALUE_VAR) {
			operandType = Operand.VAR;
		} else if (type == FalconLangAst.VALUE_ARG) {
			operandType = Operand.ARG;
		} else if (type == FalconLangAst.INVOKE_FUNC) {
			operandType = Operand.FUNC;
		} else if (type == FalconLangAst.INVOKE_FLOW) {
			operandType = Operand.FLOW;
		} else if (type == FalconLangAst.INVOKE_SYS) {
			operandType = Operand.SYS;
		} else {
			Log.log("");
			return null;
		}
		return new Operand(operandType, ast.token.value);
	}
	
	private boolean m_negation;
	private int m_x;
	private int m_block;
	private Stack<Operand> m_stack;
	private boolean[] m_allocator;
	ArrayList<FalconLangInstruction> m_instructionList;
}
